////////////////////////////////////////
//	File Header
//	Author: Stephen Ringer | stephen.ringer@gmail.com
//	File: SAGAD3D_init.cpp
//
////////////////////////////////////////

////////////////////////////////////////
//	File Includes
#include "SAGA.h"
#include "SAGAD3D.h"        // class definition
#include "SAGAD3DSkinManager.h"
#include "SAGAD3D_vchache.h"
#include <vfw.h>           // show bmp's
#include "resource.h"      // control id's
//	Library Includes
#pragma comment(lib, "vfw32.lib")
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "dxguid.lib")
//
////////////////////////////////////////

////////////////////////////////////////
//	Variables
//	To Store Results when the Dialog is closed
SAGADEVICEINFO  g_xDevice;
D3DDISPLAYMODE	g_Dspmd;
D3DFORMAT		g_fmtA;
D3DFORMAT		g_fmtB;
HBITMAP			g_hBMP;
//	Used for the Dialog Callback only
SAGAD3D *g_SAGAD3D = NULL;
bool g_bLF=false;
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: DLLEntryPoint
//	Purpose:
//		DLL Entry Point similar to WinMain()/main()
BOOL WINAPI DllEntryPoint(HINSTANCE hDll,
						  DWORD     fdwReason,
						  LPVOID    lpvReserved) 
{
	switch(fdwReason) 
	{
	  //	Called when we attach to the DLL
	case DLL_PROCESS_ATTACH:
		/* dll init/setup */
		break;
	case DLL_PROCESS_DETACH:
		/* dll shutdown/release */
		break;
	default:
		break;
	};
	return TRUE;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: CreateRenderDevice
//	Purpose:
//		To create a D3D Render Device
HRESULT CreateRenderDevice(HINSTANCE hDLL, SAGARenderDevice **pDevice)
{
	if(!*pDevice)
	{
		*pDevice = new SAGAD3D(hDLL);
		return SAGA_OK;
	}
	return SAGA_FAIL;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: ReleaseRenderDevice
//	Purpose:
//		To destroy a D3D Render Device
HRESULT ReleaseRenderDevice(SAGARenderDevice **pDevice)
{
	if(!*pDevice)
		return SAGA_FAIL;
	delete *pDevice;
	*pDevice = NULL;
	return SAGA_OK;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: SAGAD3D
//	Purpose:
//		Constructor
SAGAD3D::SAGAD3D(HINSTANCE hDLL)
{
	m_hDLL              = hDLL;
	m_pEnum             = NULL;
	m_pD3D              = NULL;
	m_pDevice           = NULL;
	m_pLog              = NULL;
	m_ClearColor        = D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f);
	m_bRunning          = false;
	m_bIsSceneRunning   = false;
	m_bUseShaders       = false;
	m_bCanDoShaders     = false;
	m_bAdditive         = false;
	m_bColorBuffer      = true;
	m_bTextures         = true;
	m_pSkinMan          = NULL;
	m_pVertexMan        = NULL;

	m_pDeclVertex       = NULL;
	m_pDeclPVertex      = NULL;
	m_pDeclLVertex      = NULL;
	m_pDeclCVertex      = NULL;
	m_pDecl3TVertex     = NULL;
	m_pDeclTVertex      = NULL;

	m_pFont             = NULL;

	//	Don't use SwapChain at first
	m_nActivehWnd       = 0;
	m_nNumVShaders      = 0;
	m_nNumPShaders      = 0;
	m_nNumFonts         = 0;

	g_SAGAD3D = this;

	m_pLog = fopen("Log_ZFXRenderDevice.txt", "w");
	Log("online (waiting for initialization call)");
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: ~SAGAD3D
//	Purpose:
//		De-constructor
SAGAD3D::~SAGAD3D()
{
	Log("Shutting Down Direct3D \n");
	if (m_pLog) fflush(m_pLog);
	Release();
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: Release
//	Purpose:
//		Release the device, D3D object, and enums
void SAGAD3D::Release()
{
	//	Our own stuff
	if (m_pEnum) 
	{
		delete m_pEnum;
		m_pEnum = NULL;
	}
	if (m_pSkinMan) 
	{
		delete m_pSkinMan;
		m_pSkinMan = NULL;
	}
	if (m_pVertexMan) 
	{
		delete m_pVertexMan;
		m_pVertexMan = NULL;
	}

	//	Shader stuff
	if (m_pDeclVertex) 
	{
		m_pDeclVertex->Release();
		m_pDeclVertex = NULL;
	}
	if (m_pDeclPVertex) 
	{
		m_pDeclPVertex->Release();
		m_pDeclPVertex = NULL;
	}
	if (m_pDeclLVertex) 
	{
		m_pDeclLVertex->Release();
		m_pDeclLVertex = NULL;
	}

	if (m_pDeclCVertex) 
	{
		m_pDeclCVertex->Release();
		m_pDeclCVertex = NULL;
	}

	if (m_pDecl3TVertex) 
	{
		m_pDecl3TVertex->Release();
		m_pDecl3TVertex = NULL;
	}

	if (m_pDeclTVertex) 
	{
		m_pDeclTVertex->Release();
		m_pDeclTVertex = NULL;
	}

	for (UINT i=0; i<m_nNumVShaders; i++) 
	{
		if (m_pVShader[i]) 
		{
			m_pVShader[i]->Release();
			m_pVShader[i] = NULL;
		}
	}
	for (UINT j=0; j<m_nNumPShaders; j++) 
	{
		if (m_pPShader[j]) 
		{
			m_pPShader[j]->Release();
			m_pPShader[j] = NULL;
		}
	}

	for (UINT k=0; k<m_nNumFonts; k++) 
	{
		if (m_pFont[k]) 
		{
			m_pFont[k]->Release();
			m_pFont[k] = NULL;
		}
	}

	if (m_pFont) 
	{
		free(m_pFont);
		m_pFont = NULL;
	}

	//	Main objects
	if(m_pDevice) 
	{
		m_pDevice->Release();
		m_pDevice = NULL;
	}
	if(m_pD3D) 
	{
		m_pD3D->Release();
		m_pD3D = NULL;
	}
	Log("offline (ok)");
	fclose(m_pLog);
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function: DlgProcWrap
//	Purpose:
//		C Function that wraps the DlgProc Function to provide to the D3D object
BOOL CALLBACK DlgProcWrap(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam)
{ return g_SAGAD3D->DlgProc(hDlg, message, wParam, lParam); }
//
////////////////////////////////////////

////////////////////////////////////////
//	Function: Init
//	Purpose
// 		Initialize dialogBox to select device and format. Dialog must
// 		have ID using -"- signs and must have combobox "IDC_ADAPTER",
// 		listbox "IDC_MODES", radioButtons "IDC_FULL" and "IDC_WND",
// 		and Buttons "IDOK" and "IDCANCEL".
// 		Parameters: 
//				HWND      - handle of application window
// 				HWND      - array to render child-window HWNDs or NULL
// 				int       - number of child HWNDs in array
// 				int       - minimum number of depth bits
// 				int       - minimum number of stencil bits
// 				bool      - create a save but slow log?
HRESULT SAGAD3D::Init(HWND hWnd, const HWND *hWnd3D, int nNumhWnd, int nMinDepth, int nMinStencil, bool bSaveLog)
{
	int nResult;

	g_bLF = bSaveLog;

	//	Use Child Windows
	if(nNumhWnd > 0)
	{
		if(nNumhWnd > MAX_3DHWND) nNumhWnd = MAX_3DHWND;
		memcpy(&m_hWnd[0], hWnd3D, sizeof(HWND)*nNumhWnd);
		m_nNumhWnd = nNumhWnd;
	}
	//	Else use Main Window Handle
	else
	{
		m_hWnd[0] = hWnd;
		m_nNumhWnd = 0;
	}
	m_hWndMain = hWnd;

	if(nMinStencil > 0) m_bStencil = true;
	Log("SAGAEngine SAGAD3D-RenderDevice Log File:\n\n");
	//	Generate Enum Object
	m_pEnum = new SAGAD3DEnum(nMinDepth, nMinStencil);
	Log("Opening dialog... \n");
	//	Load SAGA Logo
	g_hBMP = (HBITMAP)LoadImage(NULL, "SAGA.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
	//	Open up Dialog
	nResult = DialogBox(m_hDLL, "dlgChangeDevice", hWnd, DlgProcWrap);
	//	Free Resources
	if(g_hBMP) DeleteObject(g_hBMP);
	Log("Returning from dialog... \n");
	//	Error in Dialog
	if(nResult == -1) {Log("Selection dialog error \n"); return SAGA_FAIL;}
	//	Dialog Canceled
	else if (nResult == 0) {Log("Selection dialog canceled by user\n"); return SAGA_CANCELED;}
	else if (nResult == -2) { Log("Error: no compatible graphics adapter"); return SAGA_NOTCOMPATIBLE;}
	//	Dialog OK
	else {Log("Selection dialog ok - Firing up Direct3D \n"); return Go();}
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: InitWindowed
//	Purpose:
//		Parameters:
//			HWND      - handle of application window
//	        HWND      - array to render child-window HWNDs or NULL
//	        int       - number of child HWNDs in array
//	        bool      - create a save but slow log?
HRESULT SAGAD3D::InitWindowed(HWND hWnd, const HWND *hWnd3D, int nNumhWnd, bool bSaveLog) 
{
	HRESULT  hr;
	D3DCAPS9 caps;
	RECT     rc;

	g_bLF = bSaveLog;

	//	Are there any child windows to use?
	if (nNumhWnd > 0) 
	{
		if (nNumhWnd > MAX_3DHWND) nNumhWnd = MAX_3DHWND;
		memcpy(&m_hWnd[0], hWnd3D, sizeof(HWND)*nNumhWnd);
		m_nNumhWnd = nNumhWnd;
	}
	//	Else store handle to main window at least
	else 
	{
		m_hWnd[0] = hWnd;
		m_nNumhWnd = 0;
	}

	m_hWndMain  = hWnd;
	m_bWindowed = true;

	//	Build main direct3d object
	if (m_pD3D) 
	{
		m_pD3D->Release();
		m_pD3D = NULL;
	}
	m_pD3D = Direct3DCreate9( D3D_SDK_VERSION );
	if(!m_pD3D) 
	{
		Log("Error: Direct3DCreate8()");
		return SAGA_CREATEAPI;
	}

	m_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);

	if ( (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0) 
	{
		Log("Error: no hardware vertexprocessing");
		m_pD3D->Release();
		m_pD3D = NULL;
		return SAGA_NOTCOMPATIBLE;
	}

	//	Prepare present parameters structure
	ZeroMemory(&m_d3dpp, sizeof(m_d3dpp));
	m_d3dpp.Windowed               = m_bWindowed;
	m_d3dpp.BackBufferCount        = 1;
	m_d3dpp.BackBufferFormat       = D3DFMT_X8R8G8B8;
	m_d3dpp.EnableAutoDepthStencil = TRUE;
	m_d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	m_d3dpp.MultiSampleType        = D3DMULTISAMPLE_NONE;
	m_d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;
	m_bStencil = false;

	//	Windowed mode
	GetClientRect(m_hWnd[0], &rc);
	m_d3dpp.hDeviceWindow     = m_hWnd[0];
	m_d3dpp.BackBufferWidth   = rc.right;
	m_d3dpp.BackBufferHeight  = rc.bottom;

	//	Create direct3d device
	hr = m_pD3D->CreateDevice(	D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd[0], 
								D3DCREATE_HARDWARE_VERTEXPROCESSING, &m_d3dpp, &m_pDevice);


	if(FAILED(hr)) 
	{
		Log("Error: IDirect3D::CreateDevice()");
		if (hr == D3DERR_NOTAVAILABLE)
			Log("D3DERR_NOTAVAILABLE");
		else if (hr == D3DERR_INVALIDCALL)
			Log("D3DERR_INVALIDCALL");
		else if (hr == D3DERR_OUTOFVIDEOMEMORY)
			Log("D3DERR_OUTOFVIDEOMEMORY");
		else
			Log("unknown error");

		return SAGA_CREATEDEVICE;
	}

	//	Create additional swap chains if wished and possible
	if ( (m_nNumhWnd > 0) && m_bWindowed) 
	{
		for (UINT i=0; i<m_nNumhWnd; i++) 
		{
			GetClientRect(m_hWnd[i], &rc);
			m_d3dpp.hDeviceWindow    = m_hWnd[i];
			m_d3dpp.BackBufferWidth  = rc.right;
			m_d3dpp.BackBufferHeight = rc.bottom;

			hr = m_pDevice->CreateAdditionalSwapChain(&m_d3dpp, &m_pChain[i]);
			if (FAILED(hr)) break;
		}
	}

	Log("initialized (online and ready)");
	m_pDevice->GetDeviceCaps(&g_xDevice.d3dCaps);
	LogDeviceCaps(&g_xDevice.d3dCaps);

	m_bRunning        = true;
	m_bIsSceneRunning = false;
	m_dwWidth         = m_d3dpp.BackBufferWidth;
	m_dwHeight        = m_d3dpp.BackBufferHeight;

	return OneTimeInit();
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: Go
//	Purpose:
//		To Start the Direct3D device
HRESULT SAGAD3D::Go()
{
	SAGACOMBOINFO	xCombo;
	HRESULT			hr;
	HWND			hWnd;

	//	Create Direct3D Main Object
	if(m_pD3D)
	{
		m_pD3D->Release();
		m_pD3D = NULL;
	}
	m_pD3D = Direct3DCreate9( D3D_SDK_VERSION);

	if(!m_pD3D) {Log("error: Direct3DCreate9()\n"); return SAGA_CREATEAPI;}
	//	Get corresponding Combo
	for(UINT nCombo = 0; nCombo < g_xDevice.nNumCombo; nCombo++)
	{
		if(	(g_xDevice.d3dCombo[nCombo].bWindowed == m_bWindowed) &&
			(g_xDevice.d3dCombo[nCombo].d3dDevType == g_xDevice.d3dDevType) &&
			(g_xDevice.d3dCombo[nCombo].fmtAdapter == g_fmtA) &&
			(g_xDevice.d3dCombo[nCombo].fmtBackBuffer == g_fmtB))
		{
			xCombo = g_xDevice.d3dCombo[nCombo];
			break;
		}
	}
	//	Fill in Present Parameters Structure
	ZeroMemory(&m_d3dpp, sizeof(m_d3dpp));
	m_d3dpp.Windowed				= m_bWindowed;
	m_d3dpp.BackBufferCount			= 1;
	m_d3dpp.BackBufferFormat		= g_Dspmd.Format;
	m_d3dpp.EnableAutoDepthStencil	= TRUE;
	m_d3dpp.MultiSampleType			= xCombo.msType;
	m_d3dpp.AutoDepthStencilFormat	= xCombo.fmtDepthStencil;
	m_d3dpp.SwapEffect				= D3DSWAPEFFECT_DISCARD;
	//	Stencil Buffer Active?
	if(	(xCombo.fmtDepthStencil == D3DFMT_D24S8) ||
		(xCombo.fmtDepthStencil == D3DFMT_D24X4S4) ||
		(xCombo.fmtDepthStencil == D3DFMT_D15S1))
		m_bStencil = true;
	else
		m_bStencil = false;
	//	FullScreen Mode
	if(!m_bWindowed)
	{
		m_d3dpp.hDeviceWindow		= hWnd = m_hWndMain;
		m_d3dpp.BackBufferWidth		= g_Dspmd.Width;
		m_d3dpp.BackBufferHeight	= g_Dspmd.Height;
		m_d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
		ShowCursor(FALSE);
	}
	//	Windowed Mode
	else
	{
		m_d3dpp.hDeviceWindow		= hWnd = m_hWnd[0];
		m_d3dpp.BackBufferWidth		= GetSystemMetrics(SM_CXSCREEN);
		m_d3dpp.BackBufferHeight	= GetSystemMetrics(SM_CYSCREEN);
	}
	//	Create Direct3D device
	hr = m_pD3D->CreateDevice(	g_xDevice.nAdapter, g_xDevice.d3dDevType, 
								hWnd, xCombo.dwBehavior, &m_d3dpp, &m_pDevice);
	//	Create Swap Chains if needed
	if((m_nNumhWnd > 0) && m_bWindowed)
	{
		for(UINT nChain = 0; nChain < m_nNumhWnd; nChain++)
		{
			m_d3dpp.hDeviceWindow = m_hWnd[nChain];
			m_pDevice->CreateAdditionalSwapChain(&m_d3dpp, &m_pChain[nChain]);
		}
	}

	m_pEnum->~SAGAD3DEnum();
	m_pEnum = NULL;

	if(FAILED(hr)) {Log("Error: IDirect3D::CreateDevice() \n"); return SAGA_CREATEDEVICE;}
	Log("Smooth Initialization \n");
	LogDeviceCaps(&g_xDevice.d3dCaps);
	m_bRunning			= true;
	m_bIsSceneRunning	= false;
	m_dwWidth         = m_d3dpp.BackBufferWidth;
	m_dwHeight        = m_d3dpp.BackBufferHeight;
	return OneTimeInit();
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: OneTimeInit
//	Purpose:
//		Called by SAGAD3D::Go to initialize renderstates and stuff.
HRESULT SAGAD3D::OneTimeInit() 
{

	if (!SAGA3DInitCPU()) Log("no SIMD compatible CPU detected");
	else Log("SIMD compatible CPU detected => using fast math");

	//	Solid rendering
	m_ShadeMode = RS_SHADE_SOLID;

	//	Bring material and vertex cache managers online
	m_pSkinMan = new SAGAD3DSkinManager(m_pDevice, m_pLog);
	m_pVertexMan = new SAGAD3DVCManager((SAGAD3DSkinManager*)m_pSkinMan, m_pDevice, this, 8192, 8192, 
										m_pLog);

	//	Activate RenderStates
	m_pDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
	m_pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); 
	m_pDevice->SetRenderState(D3DRS_ZENABLE,  D3DZB_TRUE);
	//	Create and set standard material
	memset(&m_StdMtrl, 0, sizeof(D3DMATERIAL9));
	m_StdMtrl.Ambient.r  = 1.0f;
	m_StdMtrl.Ambient.g  = 1.0f;
	m_StdMtrl.Ambient.b  = 1.0f;
	m_StdMtrl.Ambient.a  = 1.0f;

	if (FAILED(m_pDevice->SetMaterial(&m_StdMtrl))) 
	{
		Log("Error: set material (OneTimeInit)");
		return SAGA_FAIL;
	}
	//	Set texture filtering
	m_pDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	m_pDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	m_pDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
	//	Goto 3D mode in first stage
	SAGAVIEWPORT vpView = { 0, 0, m_dwWidth, m_dwHeight };
	m_Mode   = EMD_PERSPECTIVE;
	m_nStage = -1;
	SetActiveSkinID(MAX_ID);
	//	Identity matrix for view
	IDENTITY(m_mView3D);
	//	Set clipping plane values
	SetClippingPlanes(0.1f, 1000.0f);
	//	Check and init shaders
	PrepareShaderStuff();
	//	Create a default shader. Note: its ID is 0
	if (m_bCanDoShaders) 
	{
		const char BaseShader[] =
			"vs.1.1                 \n"\
			"dcl_position0 v0       \n"\
			"dcl_normal0   v3       \n"\
			"dcl_texcoord0 v6       \n"\
			"m4x4 oPos, v0, c0      \n"\
			"mov oD0, c4            \n"\
			"mov oT0, v6            \n";

		if (FAILED(CreateVShader((void*)BaseShader, sizeof(BaseShader),
			false, false, NULL))) {
				Log("CreateVShader() default failed");
				return SAGA_FAIL;
		}
		/*
		if (FAILED(ActivateVShader(0, VID_UU))) {
		Log("ActivateVShader() default failed");
		return SAGA_FAIL;
		}
		*/
	}
	//	Set Ambient light level
	SetAmbientLight(1.0f, 1.0f, 1.0f);

	SetTextureStage(0, RS_TEX_MODULATE);
	SetTextureStage(1, RS_NONE);
	SetTextureStage(2, RS_NONE);
	SetTextureStage(3, RS_NONE);
	SetTextureStage(4, RS_NONE);
	SetTextureStage(5, RS_NONE);
	SetTextureStage(6, RS_NONE);
	SetTextureStage(7, RS_NONE);

	//	Set perspective projection stage 0
	if (FAILED(InitStage(0.8f, &vpView, 0))) return SAGA_FAIL;
	//	Set perspective projection
	if (FAILED(SetMode(EMD_PERSPECTIVE, 0))) return SAGA_FAIL;
	//	World matrix to identity, this is needed to set
	//		correct values to vertex shader registers also
	SetWorldTransform(NULL);

	Log("one time scene init complete");
	return SAGA_OK;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: Log
//	Purpose:
//		To Write OutputString to attribut OutputStream if 
//		Parameter: FILE - pointer to LogFile stream or NULL
void SAGAD3D::Log(char *chString, ...) 
{
	char ch[256];
	char *pArgs;

	pArgs = (char*) &chString + sizeof(chString);
	vsprintf_s(ch, chString, pArgs) ;
	fprintf(m_pLog, "[ZFXD3DDevice]: ");
	fprintf(m_pLog, ch);
	fprintf(m_pLog, "\n");

	if(g_bLF) fflush(m_pLog);
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: DlgProc
//	Purpose:
//		Callback function to handle the device/mode selection dialog
BOOL CALLBACK SAGAD3D::DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	DIBSECTION	dibSection;
	HRESULT		hr;
	BOOL		bWnd = FALSE;

	//	Get Handles
	HWND hFULL			= GetDlgItem(hDlg, IDC_FULL);
	HWND hWND			= GetDlgItem(hDlg, IDC_WND);
	HWND hADAPTER		= GetDlgItem(hDlg, IDC_ADAPTER);
	HWND hMODE			= GetDlgItem(hDlg, IDC_MODE);
	HWND hADAPTERFMT	= GetDlgItem(hDlg, IDC_ADAPTERFMT);
	HWND hBACKFMT		= GetDlgItem(hDlg, IDC_BACKFMT);
	HWND hDEVICE		= GetDlgItem(hDlg, IDC_DEVICE);

	switch(message)
	{
		//	Preselect Windowed Mode
	case WM_INITDIALOG:
		{
			SendMessage(hWND, BM_SETCHECK, BST_CHECKED, 0);
			hr = m_pEnum->Enum(hADAPTER, hMODE, hDEVICE, hADAPTERFMT, hBACKFMT, hWND, hFULL, m_pLog);
			if (hr == SAGA_NOTCOMPATIBLE) EndDialog(hDlg, -2);
			else if (hr == SAGA_FAIL) EndDialog(hDlg, -1);
			return TRUE;
		}
		//	Render logo(g_hBMP is initialized in Init())
	case WM_PAINT:
		{
			if(g_hBMP)
			{
				GetObject(g_hBMP, sizeof(DIBSECTION), &dibSection);
				HDC			hdc = GetDC(hDlg);
				HDRAWDIB	hdd = DrawDibOpen();
				DrawDibDraw(hdd, hdc, 50, 10, 95, 99, 
							&dibSection.dsBmih,
							dibSection.dsBm.bmBits, 0, 0,
							dibSection.dsBmih.biWidth,
							dibSection.dsBmih.biHeight, 0);
				DrawDibClose(hdd);
				ReleaseDC(hDlg, hdc);
			}
		} break;
		//	A Control reports a message
	case WM_COMMAND:
		{
			switch(LOWORD(wParam))
			{
				//	OK button
			case IDOK:
				{
					m_bWindowed = SendMessage(hFULL, BM_GETCHECK, 0, 0) != BST_CHECKED;
					m_pEnum->GetSelections(&g_xDevice, &g_Dspmd, &g_fmtA, &g_fmtB);
					GetWindowText(hADAPTER, m_chAdapter, 256);
					EndDialog(hDlg, 1);
					return TRUE;
				}break;
				//	CANCEL button
			case IDCANCEL:
				{
					EndDialog(hDlg, 0);
					return TRUE;
				}break;
			case IDC_ADAPTER:
				{
					if(HIWORD(wParam)==CBN_SELCHANGE)
						m_pEnum->ChangedAdapter();
				}break;
			case IDC_DEVICE:
				{
					if(HIWORD(wParam)==CBN_SELCHANGE)
						m_pEnum->ChangedDevice();
				}break;
			case IDC_ADAPTERFMT:
				{
					if(HIWORD(wParam)==CBN_SELCHANGE)
						m_pEnum->ChangedAdapterFmt();
				}break;
			case IDC_FULL: case IDC_WND:
				{
					m_pEnum->ChangedWindowMode();
				}break;
			//	End CMD Switch
			}
		}break;
	//	End MSG Switch
	}
	return FALSE;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: LogDeviceCaps
//	Purpose:
//		Log all nice features of a device for eye candy
void SAGAD3D::LogDeviceCaps(D3DCAPS9 *pCaps) 
{
	Log("ADAPTERINFO: {");

	if (pCaps->DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) 
	{
		Log("   adapter features hardware TnL");
		if (pCaps->DevCaps & D3DDEVCAPS_PUREDEVICE) Log("   (pure device possible)");
		else Log("   (no pure device possible)");
	}
	else
		Log("   adapter only features software TnL");
	//	TEXTURE STUFF
	Log("   max. texture stages: %d" , pCaps->MaxTextureBlendStages);
	Log("   max. textures for single pass: %d" , pCaps->MaxSimultaneousTextures);
	Log("   max. texture width: %d" , pCaps->MaxTextureWidth);
	Log("   max. texture height: %d" , pCaps->MaxTextureHeight);
	//	VERTEX SHADER VERSION
	if (pCaps->VertexShaderVersion < D3DVS_VERSION(1,1) )
		Log("   Vertex Shader Version 1.0");
	else if (pCaps->VertexShaderVersion < D3DVS_VERSION(2,0) )
		Log("   Vertex Shader Version 1.1");
	else  
		Log("   Vertex Shader Version 2.0 or better");
	//	PIXEL SHADER VERSION
	if (pCaps->PixelShaderVersion < D3DPS_VERSION(1,1) )
		Log("   Pixel Shader Version 1.0");
	else if (pCaps->PixelShaderVersion < D3DPS_VERSION(1,2) )
		Log("   Pixel Shader Version 1.1");
	else if (pCaps->PixelShaderVersion < D3DPS_VERSION(1,3) )
		Log("   Pixel Shader Version 1.2");
	else if (pCaps->PixelShaderVersion < D3DPS_VERSION(1,4) )
		Log("   Pixel Shader Version 1.3");
	else if (pCaps->PixelShaderVersion < D3DPS_VERSION(2,0) )
		Log("   Pixel Shader Version 1.4");
	else
		Log("   Pixel Shader Version 2.0 or better");
	Log("   } ENDINFO");
	// SCREEN RESOLUTION, FORMAT AND STUFF
	Log("DISPLAYINFO {");
	LPDIRECT3DSURFACE9 pDepthStencil=NULL;
	D3DSURFACE_DESC desc;
	D3DFORMAT Format = D3DFMT_UNKNOWN;
	D3DDISPLAYMODE mode = {0,0,0,D3DFMT_UNKNOWN};

	if (FAILED(m_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &mode)))
		Log("Error: IDirect3D::GetAdapterDisplayMode failed");

	if (FAILED(m_pDevice->GetDepthStencilSurface(&pDepthStencil)))
		Log("Error: IDirect3DDevice::GetDepthStencilSurface failed");
	else 
	{
		if (FAILED(pDepthStencil->GetDesc(&desc))) 
		{
			Log("Error: IDirect3DSurface::GetDesc failed");
			Format = D3DFMT_UNKNOWN;
		}
	}

	Log("   Resolution: %dx%d", mode.Width, mode.Height);
	Log("   Refreshrate: %d", mode.RefreshRate);

	//	BackBuffer format
	switch(mode.Format) 
	{
		case D3DFMT_A2R10G10B10: Log("   Pixelformat: D3DFMT_A2R10G10B10"); break;
		case D3DFMT_A8R8G8B8:    Log("   Pixelformat: D3DFMT_A8R8G8B8"); break;
		case D3DFMT_X8R8G8B8:    Log("   Pixelformat: D3DFMT_X8R8G8B8"); break;
		case D3DFMT_A1R5G5B5 :   Log("   Pixelformat: D3DFMT_A1R5G5B5"); break;
		case D3DFMT_X1R5G5B5:    Log("   Pixelformat: D3DFMT_X1R5G5B5"); break;
		case D3DFMT_R5G6B5:      Log("   Pixelformat: D3DFMT_R5G6B5");   break;
		default: break;
	} 

	//	DepthBuffer format
	switch(desc.Format) 
	{
		case D3DFMT_D16_LOCKABLE:  Log("   Depth/Stencil: D3DFMT_D16_LOCKABLE");  break;
		case D3DFMT_D32F_LOCKABLE: Log("   Depth/Stencil: D3DFMT_D32F_LOCKABLE"); break;
		case D3DFMT_D32:     Log("   Depth/Stencil: D3DFMT_D32");     break;
		case D3DFMT_D15S1:   Log("   Depth/Stencil: D3DFMT_D15S1");   break;
		case D3DFMT_D24S8:   Log("   Depth/Stencil: D3DFMT_D24S8");   break;
		case D3DFMT_D24X8:   Log("   Depth/Stencil: D3DFMT_D24X8");   break;
		case D3DFMT_D24X4S4: Log("   Depth/Stencil: D3DFMT_D24X4S4"); break;
		case D3DFMT_D24FS8:  Log("   Depth/Stencil: D3DFMT_D24FS8");  break;
		case D3DFMT_D16:     Log("   Depth/Stencil: D3DFMT_D16");     break;
		default: break;
	} 

	pDepthStencil->Release();
	Log("   } ENDINFO");
}
//
////////////////////////////////////////